/*
PhysicsEngine.h
Physics Engine declaration

Project: surface-physics : http://code.google.com/p/surface-physics/

Description :This file declares the interface for the physics engine as a part of the Bump
plugin. The PhysicsEngine class maintains the entire physics world and allocates partitions
to bases, vessels etc. It will also be used to maintain large collision spaces temporarily
for collisions in outer space. The rigid body representing the hull of the vessel will be
created in the collision space to collide with other objects

Positions will be maintained in float range through use of a moving origin close to the
colliding region in Orbiter global co-ords and conversion of co-ords back and forth.

Written by Abhijit Nandy
License : Apache License 2.0

*/

#pragma once

#include "orbitersdk.h"

#include "../Visualizer/GlutDemoApplication.h"

#include "LinearMath/btAlignedObjectArray.h"
#include "btBulletDynamicsCommon.h"
#include "../Visualizer/GlutStuff.h"

#include <list>
#include <unordered_map>


class btBroadphaseInterface;
class btCollisionShape;
class btOverlappingPairCache;
class btCollisionDispatcher;
class btConstraintSolver;
struct btCollisionAlgorithmCreateFunc;
class btDefaultCollisionConfiguration;

//For allowing Bump_BaseObject to register an external(to this class) render function
class Bump_BaseObject;
typedef std::list<Bump_BaseObject*> RenderObjectList;

//Viewer modes
enum PhysicsMode {
	PM_SILENT,
	PM_VISUALIZE,
	PM_MESH_TEST,
	PM_BASE_CONSTRUCTION
};

//Terrain types currently supported
enum TerrainType {
	TT_NONE,
	TT_PLANE,
	TT_PLANETCURVE,
	TT_BOX,
	TT_MSH,
	TT_TRIMESH,
	TT_ORULEX,
	TT_D3D11
};

/**
 * Visualizer camera modes
 */
enum VisualizerCameraMode {
	VCM_FREE,
	VCM_ORBITER,
	VCM_GUI,

	// ----- Add camera modes above this line ------
	MAX_CAMERA_MODES
};


#define NO_KEY -1

/**
 * The main collision structure which is updated by the nearphase() callback
 * from Bullet's collision pipeline. It's created by the Bump_BaseObject which
 * needs to know about other objects colliding with it & stored as the user pointer
 * in the main rigid body for that base object. It allows recording multiple colliding
 * objects (upto MAX_COLLISIONS) which can later be examined by the base object.
 *
 * This may be converted to a full blown class later to support filtering out certain
 * types of objects, but for performance reasons we haven't done so yet.
 */
struct CollisionInfo
{
	char *strObjectName;

	typedef std::tr1::unordered_map<btRigidBody*, int> CollisionMap;
	CollisionMap collMap;

	// Set when at least 1 hit has occurred, reset before every iteration
	bool colliding;

	void resetCollisionInfo(void)
	{
		collMap.clear();
		colliding = false;
	}
};

/*
 * --------------------------Orbiter Interface ----------------------------
 * Declare all globals in .cpp file as extern here, so they can be set from outside
 * Do not set any values here as it will lead to 'multiple redefinitions' errors
 */

//These are declared in Bump module & is needed by the PhysicsEngine class
extern PhysicsMode mode;
extern DWORD baseCmd;
extern bool stepSim;


/* --------------------- End of Orbiter Interface ---------------------- */


//Useful macros
#define BT3ARGS(a)	(a).x(), (a).y(), (a).z()
#define BTZEROVECTOR btVector3(0., 0., 0.)

const int MAX_RENDER_OBJECTS = 20;

/**
 * The physics engine class that steps physics and uses GLUT to visualize
 */
class PhysicsEngine : public GlutDemoApplication
{

	//General Physics world variables	
	class btBroadphaseInterface*	m_overlappingPairCache;
	class btCollisionDispatcher*	m_dispatcher;
	class btConstraintSolver*	m_constraintSolver;
	class btDefaultCollisionConfiguration* m_collisionConfiguration;
	class btTriangleIndexVertexArray*	m_indexVertexArrays;
	btVector3*	m_vertices;

	btCollisionShape	*groundShape, *supportPlaneShape;
	btRigidBody* groundRigidBody; //WARNING, not used !!




	//Camera control
	btScalar	m_cameraHeight;
	btScalar	m_minCameraDistance;
	btScalar	m_maxCameraDistance;
	VisualizerCameraMode	mCameraMode;
	btRigidBody *mFocusBody[MAX_CAMERA_MODES];




public:

	//Needs to be public for the MSH importer
	btAlignedObjectArray<btCollisionShape*> m_collisionShapes;

	// do not allow continuous focus updates when free roving camera active
	// A null target body should not be passed in here, if so then we have an invalid rigid body somewhere
	// A non-null rigid body can cause a crash too if its deleted after focus is set
	// Also we had this issue : To move focus away from a rigid body which is about to be disabled
	// we passed in another rigid body(base ground patch). But this body was null, so the focusBody remained
	// pointing to the old body which got deleted ! we got a crash soon after.
	// So we must pass in a valid body each time we setFocusBody() & before current focus body is deleted !
	// So what should happen is, when the current focus is deleted, focus should shift to some base held
	// rigid body - this is an independent body managed by base - not in the list of objects - so it cant be
	// deleted and is valid from beginning to end of the sim. In the next iteration focus will shift to
	// another body IF another body sets focus to itself based on whether Orbiter
	// has focus on it. Else we will remain focused on the base focus body.
	void setCameraFocus(btRigidBody *targetBody, VisualizerCameraMode mode) {

		if (targetBody == NULL) {
			mCameraMode = VCM_FREE;
			oapiWriteLogV("PhysicsEngine::setCameraFocus : ERROR targetBody is now %p, set FREE camera",
					targetBody);
		}
		

		mFocusBody[mode] = targetBody; // 0th position is always null (VCC_FREE)

		//oapiWriteLogV("setOrbiterFocusBody : WARN mFocusBody[%d] changed to : %p", mode, targetBody);
	}


	/* The list of base objects whose renderme() should be called to draw additional shapes
	 * not in the dynamics world(like wheels of a raycast vehicle)
	 * A list is more efficient as objects might de-register mid-sim, causing deletion from list middle.
	 */
	RenderObjectList renderObjList;
	RenderObjectList::iterator renderObjIter;

	//External rendering object registration
	void addExternalRenderObject(Bump_BaseObject *bbObj, RenderObjectList::iterator& renderFuncPos)
	{
		renderObjList.push_back(bbObj);
		renderObjIter = renderObjList.end();
		--renderObjIter;
		renderFuncPos = renderObjIter;
	}

	//External rendering object de-registration, can be used if a base object has requested
	//rendering but is being removed mid-sim, can be called from its destructor.
	//STL guarantees that if external list iterators are being maintained to the list then
	//they wont be invalidated even if some element before them was removed.
	void removeExternalRenderObject(RenderObjectList::iterator& pos)
	{ renderObjList.erase(pos); }



	PhysicsEngine();
	virtual ~PhysicsEngine();

	void	initPhysics();


	//Rigid body management
	btRigidBody* addRigidBody(float mass, const btTransform& startTransform,btCollisionShape* shape,
			btVector3& bodyInertia = BTZEROVECTOR);
	btRigidBody* addKinematicBody(btScalar mass, const btTransform& startTransform,btCollisionShape* shape);

	void 	getTransforms(void);

	void clientMoveAndDisplay();
	void displayCallback();
	void renderme();
	void WMCloseFuncCallback();
	void clientResetScene();

	/**
	 * Used to focus on specific objects, camera control through f9/10/11 keys
	 */
	void updateCamera();

	void specialKeyboard(int key, int x, int y);
	void specialKeyboardUp(int key, int x, int y);

	//Used by defineGround() which is called externally by BulletBase
	void addSupportPlane(void);

	//Orbiter msh tools
	btRigidBody* addOrbiterMshDynamic(char *fileName, btVector3& offset);
	btRigidBody* addOrbiterMshStatic(char *fileName, btVector3& offset);
	btCollisionShape* importMsh(char *fileName);

	//Triangle mesh, can be removed
	btCollisionShape* defineTriMeshShape(void);
	int  defineVehicle(void);
	int  defineGround(TerrainType terrainType);
	
	void updateVehicle(void);



	static DemoApplication* Create()
	{
		PhysicsEngine* demo = new PhysicsEngine;
		demo->myinit();
		demo->initPhysics();
		return demo;
	}

	btDynamicsWorld* getDynamicWorld(void)
	{ return m_dynamicsWorld; }

	GL_ShapeDrawer* getShapeDrawer(void)
		{ return m_shapeDrawer; }


	btAlignedObjectArray<btCollisionShape*>* getCollisionShapesArray(void)
	{ return &m_collisionShapes; }

	//Prints the current state of the physics engine
	void print(void);



	
};


